vlwkaos' digital garden

TypeScript - Logical operator narrowing

배열이 undefined가 아니라면 배열의 길이를 반환하는 함수를 구현해보자. 가장 먼저 떠오르는 것은 undefined에 대한 분기를 통해 반환하는 것이다. 하지만 이렇게도 구현할 수 있다.

function arrayLength(strings: string[] | undefined): number | undefined {
  return strings && strings.length;
}
[arrayLength(['a', 'b']), arrayLength(undefined)];

이렇게 하면 false가 반환되어야 하지 않냐고 생각할 수도 있다. 계속 읽어보자.


[[JavaScript]][[TypeScript]]에서 && 연산자는 단순히 Boolean에 대한 연산만 하는게 아니다. 아래 값들은 모두 false 대신 이용될 수 있다.

  • false
  • 0
  • 0n (This is a "BigInt", which is part of ECMAScript 2020.)
  • ''
  • undefined
  • null
  • NaN.

이외의 모든 값은 true로 작동한다. 예를들어 0false 로 사용될 수 있고, 57true로 이용될 수 있다. 이게 왜 중요하냐면,

&& 연산자는 연산을 가장 처음으로 거짓을 만드는 값을 반환한다. 만약 둘다 true일 경우 두번째 값을 반환한다.

true && 'hello'; // 'hello'

i.e. 위의 arrayLength함수는 undefined가 주어졌을 때 그대로 undefined를 반환한다.


||의 용도는 익히 알려져있다. 가장 먼저 true로 연산되게 하는 값을 반환한다. 그래서 [[nullish coalescing]] 연산자 ??가 등장하기 전에 유효한 값을 지정하기 위해 많이 사용되었다(false 대신 사용될 수 있는 값 중 ''0이 있어 위험요소로 작용할 수 있었다). 반대로 false 로 연산되는 경우에는 나중의 값을 반환한다.

false || null; // null

||&& 가 잘못 사용되는 경우가 있는데, TypeScript는 컴파일러가 타입 추론을 통해 일부 에러를 미리 알려줄 수 있다. 예를 들어

function arrayLength(strings: string[] | undefined): number | undefined {
  return strings || strings.length;
}

의 함수에 undefined의 값이 인자로 전달되었다면, || 연산시 첫번째는 false로 패스하고 우측을 계산할 것이다. 그러나 이때 string[] | undefined의 타입 중 undefined에 대해 .length라는 속성이 있을 수 없기 때문에 컴파일러에서 에러를 알려준다.

이런식으로 TypeScript는 논리연산자에 대해 타입 추론을 하여 그 범위를 좁혀줌으로써 우리가 좀 더 안전하게 코딩할 수 있도록 도와준다.

TypeScript - Logical operator narrowing